package step

import (
	"crypto/sha256"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	schcomm "github.com/wanchain/go-mpc/storeman/schnorrcomm"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"strconv"
)

type MpcRSKShare_StepUni struct {
	BaseMpcStep
	RSkErrNum uint16
	sigNum    int
}

func CreateMpcRSKShareStepUni(degree int, totalNum int, sigNum int) *MpcRSKShare_StepUni {
	mpc := &MpcRSKShare_StepUni{*CreateBaseMpcStep(totalNum, 1), 0, sigNum}
	mpc.messages = make([]MpcMessageGenerator, sigNum)
	for i := 0; i < sigNum; i++ {
		mpc.messages[i] = createSkPolyGenUni(degree, totalNum, i, mpc.SchnorrMpcer())
	}
	return mpc
}

func (rss *MpcRSKShare_StepUni) CreateMessage() []mpcprotocol.StepMessage {
	// data: bigInt + R(bigInt) + S(bigInt)
	message := make([]mpcprotocol.StepMessage, rss.totalNum)
	_, grpIdstr, _ := rss.BaseStep.GetGrpId()
	sigNum := rss.sigNum

	for i := 0; i < rss.totalNum; i++ {

		message[i].MsgCode = mpcprotocol.MPCMessage
		message[i].Data = make([]big.Int, 3*sigNum)
		nodeId, _ := osmconf.GetOsmConf().GetNodeIdByIndex(grpIdstr, uint16(i))
		message[i].PeerID = nodeId

		for sigIndex := 0; sigIndex < sigNum; sigIndex++ {
			skpv := rss.messages[sigIndex].(*RandomPolynomialGenUni)
			if schcomm.MaliceRContent {
				message[i].Data[sigIndex*3] = *schcomm.BigOne
			} else {
				message[i].Data[sigIndex*3] = skpv.polyValue[i]
			}

			message[i].Data[sigIndex*3+1] = *skpv.polyValueSigR[i]
			if schcomm.MaliceRSig {
				message[i].Data[sigIndex*3+2] = *schcomm.BigOne // used to simulate R stage malice
			} else {
				message[i].Data[sigIndex*3+2] = *skpv.polyValueSigS[i]
			}
		}
	}

	return message
}

func (rss *MpcRSKShare_StepUni) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {
	err := rss.BaseMpcStep.FinishStep()
	if err != nil {
		return err
	}

	rcvCollection, err := rss.buildRcvedCollection()
	if err != nil {
		log.SyslogErr("MpcRSKShare_StepUni::FinishStep", "buildRcvedCollection err", err.Error(), "ctx", rss.GetMpcContextId())
		return err
	}

	err = rss.wrRcvedCollection(rcvCollection)
	if err != nil {
		log.SyslogErr("MpcRSKShare_StepUni::FinishStep", "wrRcvedCollection err", err.Error(), "ctx", rss.GetMpcContextId())
		return err
	}
	log.SyslogInfo("MpcRSKShare_StepUni::FinishStep save RcvedCollection",
		" rcvCollection", hexutil.Encode(rcvCollection.Bytes()), "ctx", rss.GetMpcContextId())
	err = rss.wrSkSIJ()
	if err != nil {
		log.SyslogErr("MpcRSKShare_StepUni::FinishStep", " wrSkShare err", err.Error(), "ctx", rss.GetMpcContextId())
		return err
	}

	return nil
}

func (rss *MpcRSKShare_StepUni) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	log.SyslogDebug("MpcRSKShare_StepUni::HandleMessage Entering", " msg", msg, "ctx", rss.GetMpcContextId())
	_, grpIdString, _ := rss.BaseStep.GetGrpId()

	senderPk, _ := osmconf.GetOsmConf().GetPKByNodeId(grpIdString, msg.PeerID)
	err := schcomm.CheckPK(senderPk)
	if err != nil {
		log.SyslogErr("MpcRSKShare_StepUni::HandleMessage", " HandleMessage", err.Error(), "ctx", rss.GetMpcContextId())
	}

	senderIndex, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, msg.PeerID)
	selfIndex, err := osmconf.GetOsmConf().GetSelfInx(grpIdString)
	if err != nil {
		log.SyslogErr("MpcRSKShare_StepUni::HandleMessage", " HandleMessage.GetSelfInx", err.Error(), "ctx", rss.GetMpcContextId())
	}

	sigNum := rss.sigNum
	for i := 0; i < sigNum; i++ {
		// get data, r, s
		sij := msg.Data[3*i]
		r := msg.Data[3*i+1]
		s := msg.Data[3*i+2]

		// 1. check sig
		h := sha256.Sum256(sij.Bytes())
		bVerifySig := schcomm.VerifyInternalData(senderPk, h[:], &r, &s)

		bContent := true

		if !bVerifySig {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage:VerifyInternalData",
				" verify sk sig fail", msg.PeerID.SlimString(),
				"groupId", grpIdString,
				"senderPK", hexutil.Encode(crypto.FromECDSAPub(senderPk)),
				"senderIndex", senderIndex,
				"recieverIndex", selfIndex,
				"R", hexutil.Encode(r.Bytes()),
				"S", hexutil.Encode(s.Bytes()),
				"h[:]", hexutil.Encode(h[:]), "ctx", rss.GetMpcContextId())
		} else {
			log.SyslogDebug("MpcRSKShare_StepUni::HandleMessage:VerifyInternalData",
				" verify sk sig successfully", msg.PeerID.SlimString(),
				"groupId", grpIdString,
				"senderPK", hexutil.Encode(crypto.FromECDSAPub(senderPk)),
				"senderIndex", senderIndex,
				"recieverIndex", selfIndex,
				"R", hexutil.Encode(r.Bytes()),
				"S", hexutil.Encode(s.Bytes()),
				"h[:]", hexutil.Encode(h[:]), "ctx", rss.GetMpcContextId())
		}

		// 2. check sij*G=si+a[i][0]*X+a[i][1]*X^2+...+a[i][n]*x^(n-1)
		selfNodeId, _ := osmconf.GetOsmConf().GetSelfNodeId()
		xValue, err := osmconf.GetOsmConf().GetXValueByNodeId(grpIdString, selfNodeId, rss.SchnorrMpcer())
		if err != nil {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage", "GetXValueByNodeId", err.Error(), "ctx", rss.GetMpcContextId())
		}

		// get sender poly commit
		keyPolyCMG := mpcprotocol.RPolyCMG + strconv.Itoa(int(senderIndex))
		pgBytes, _ := rss.GetMpcResult().GetByteValue(keyPolyCMG)

		pks, err := rss.SchnorrMpcer().SplitPksFromBytes(pgBytes[:])
		if err != nil {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage",
				" polyCMG GetBytevalue error", err.Error(), "ctx", rss.GetMpcContextId())

			bContent = false
		}

		threshold, _ := osmconf.GetOsmConf().GetThresholdNum(grpIdString)
		if threshold < 1 {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage before evalByPolyG threshold should be greater 1", "threshold", threshold, "ctx", rss.GetMpcContextId())
			return true
		}
		if len(pks) != sigNum*int(threshold) {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage before evalByPolyG", "len(pks)", len(pks), "threshold", threshold, "ctx", rss.GetMpcContextId())
			return true
		}
		sigPks := pks[i*int(threshold) : (i+1)*int(threshold)]
		sijgEval, err := rss.SchnorrMpcer().EvalByPolyG(sigPks, uint16(len(sigPks)-1), xValue)
		if err != nil {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage schnorrmpc.EvalByPolyG", "error", err.Error(), "ctx", rss.GetMpcContextId())
			return true
		}

		sijg, _ := rss.SchnorrMpcer().SkG(&sij)
		if !rss.SchnorrMpcer().Equal(sijg, sijgEval) {
			bContent = false
		}

		oldErrNum := rss.RSkErrNum
		if !bContent || !bVerifySig {
			// check Not success
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage",
				" verify sk data fail", msg.PeerID.SlimString(),
				"groupId", grpIdString, "ctx", rss.GetMpcContextId())

			rss.RSkErrNum += 1

			//error s[i][j]
			log.SyslogInfo("MpcRSKShare_StepUni::HandleMessage RSkErr Info", "ErrNum", rss.RSkErrNum, "ctx", rss.GetMpcContextId())
			if rss.RSkErrNum >= 1 {
				rskErrInfo := make([]big.Int, 6)
				// sendIndex, rvcIndex, s[i][j], r, s
				rskErrInfo[0] = *big.NewInt(0).SetInt64(int64(senderIndex))
				rskErrInfo[1] = *big.NewInt(0).SetInt64(int64(selfIndex))
				rskErrInfo[2] = sij
				rskErrInfo[3] = r
				rskErrInfo[4] = s
				rskErrInfo[5] = *big.NewInt(0).SetInt64(int64(i))

				keyErrInfo := mpcprotocol.RSkErrInfos + strconv.Itoa(int(rss.RSkErrNum)-1)
				log.SyslogErr("MpcRSKShare_StepUni::HandleMessage RSkErr Info", "key", keyErrInfo, "error info", rskErrInfo, "ctx", rss.GetMpcContextId())
				err := rss.GetMpcResult().SetValue(keyErrInfo, rskErrInfo)
				if err != nil {
					log.SyslogErr("MpcRSKShare_StepUni::HandleMessage RSkErr Info SetValue Failed", "key", keyErrInfo, "error", err.Error(), "ctx", rss.GetMpcContextId())
				} else {
					log.SyslogErr("MpcRSKShare_StepUni::HandleMessage RSkErr Info SetValue success", "key", keyErrInfo, "ctx", rss.GetMpcContextId())
				}
			}
		}

		if rss.RSkErrNum > oldErrNum {
			keyErrNum := mpcprotocol.RSkErrNum
			rskErrInfoNum := make([]big.Int, 1)
			rskErrInfoNum[0] = *big.NewInt(0).SetInt64(int64(rss.RSkErrNum))
			rss.GetMpcResult().SetValue(keyErrNum, rskErrInfoNum)
		}

		skpv := rss.messages[i].(*RandomPolynomialGenUni)
		_, exist := skpv.message[*msg.PeerID]
		if exist {
			log.SyslogErr("MpcRSKShare_StepUni::HandleMessage", " can't find msg . peerID",
				msg.PeerID.SlimString(), " PeerID", *msg.PeerID, "ctx", rss.GetMpcContextId())
			return false
		}
		//todo msg.Data[]   sij
		skpv.message[*msg.PeerID] = msg.Data[0] //message.Value
	}
	log.SyslogDebug("MpcRSKShare_StepUni::HandleMessage Leaving", " msg", msg, "ctx", rss.GetMpcContextId())
	return true
}

func (rss *MpcRSKShare_StepUni) buildRcvedCollection() (*big.Int, error) {
	skpv := rss.messages[0].(*RandomPolynomialGenUni)
	_, grpIdString, _ := rss.BaseStep.GetGrpId()

	bigs := make([]big.Int, 0)
	for peerId, _ := range skpv.message {
		senderIndex, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, &peerId)
		bigs = append(bigs, *big.NewInt(0).SetUint64(uint64(senderIndex)))
	}
	return osmconf.BuildDataByIndexes(&bigs)
}

func (rss *MpcRSKShare_StepUni) wrRcvedCollection(rcvCol *big.Int) error {

	err := rss.GetMpcResult().SetValue(mpcprotocol.RRcvedColl, []big.Int{*rcvCol})
	if err != nil {
		log.SyslogErr("MpcRSKShare_StepUni::wrRcvedCollection", "wrRcvedCollection error ", err.Error(), "ctx", rss.GetMpcContextId())
		return err
	}
	return nil
}

func (rss *MpcRSKShare_StepUni) wrSkSIJ() error {

	sigNum := rss.sigNum
	for i := 0; i < sigNum; i++ {
		skpv := rss.messages[i].(*RandomPolynomialGenUni)
		_, grpIdString, _ := rss.BaseStep.GetGrpId()

		for peerId, sij := range skpv.message {
			senderIndex, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, &peerId)
			key := mpcprotocol.RSKSIJ + strconv.Itoa(int(senderIndex)) + strconv.Itoa(i)
			err := rss.GetMpcResult().SetValue(key, []big.Int{sij})
			if err != nil {
				log.SyslogErr("\t\t\tMpcRSKShare_StepUni::wrSkSIJ", "wrSkSIJ error ", err.Error(), "ctx", rss.GetMpcContextId())
				return err
			}
			log.SyslogInfo("\t\t\tMpcRSKShare_StepUni::wrSkSIJ", "key", key, "value", "0xxxxx", "ctx", rss.GetMpcContextId())
		}
	}

	return nil
}
